package com.hrbxlh.support.web.attendance.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Table;
import com.hrbxlh.enums.RoleEnum;
import com.hrbxlh.exception.GlobalException;
import com.hrbxlh.pojo.SysUser;
import com.hrbxlh.support.attendance.dao.AttenceClassCourseMapper;
import com.hrbxlh.support.attendance.dao.AttendanceMapper;
import com.hrbxlh.support.attendance.dao.AttendanceMapperExt;
import com.hrbxlh.support.attendance.domain.AttenceClassCourse;
import com.hrbxlh.support.attendance.domain.AttenceClassCourseExample;
import com.hrbxlh.support.attendance.domain.Attendance;
import com.hrbxlh.support.attendance.domain.AttendanceExample;
import com.hrbxlh.support.web.attendance.aop.PreAcquireLock;
import com.hrbxlh.support.web.attendance.po.RecordPO;
import com.hrbxlh.support.web.attendance.service.AttenceService;
import com.hrbxlh.support.web.attendance.vo.AttenceConfigVO;
import com.hrbxlh.support.web.attendance.vo.ClassCourseVO;
import com.hrbxlh.util.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hrbxlh.support.web.attendance.constant.AttendanceConstant.ATTENDANCE;

/**
 * 考勤
 *
 * @author lx
 */
@Service
@Slf4j
public class AttenceServiceImpl implements AttenceService {

    @Autowired
    private AttendanceMapper attendanceMapper;

    @Autowired
    private AttenceClassCourseMapper attenceClassCourseMapper;

    @Autowired
    private AttendanceMapperExt attendanceMapperExt;

    @Override
    public List<AttenceConfigVO> listUserAttendance(SysUser user) {

        Date date = new Date();

        Integer userId = user.getUserNum();
        if (!Objects.equals(RoleEnum.STUDENT.getCode(), user.getRoleId())) {
            return null;
        }

        // 获取学生班级及课程
        List<ClassCourseVO> classCourseVOS = queryStudentClassCourse(user.getStuorteaId());
        if (CollectionUtils.isEmpty(classCourseVOS)) return null;

        // 根据课程与班级的数据，获取课程的配置
        AttenceClassCourseExample example = new AttenceClassCourseExample();
        classCourseVOS.forEach(courseDTO -> {
            AttenceClassCourseExample.Criteria criteria1 = example.createCriteria();
            criteria1.andCourseIdEqualTo(courseDTO.getCourseId()).andClassIdEqualTo(courseDTO.getClassId());
            example.or(criteria1);
        });
        List<AttenceClassCourse> attenceClassCourses = attenceClassCourseMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(attenceClassCourses)) return null;

        // 获取今日考勤时间段
        List<Integer> ids = attenceClassCourses.stream()
                .filter(attenceClassCourse -> attenceClassCourse.getSwitchOn())
                .map(AttenceClassCourse::getId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ids)) return null;
        List<AttenceConfigVO> attenceConfigs = attendanceMapperExt.queryAttenceConfig(
                DateUtil.now(), DateUtil.dayOfWeekEnum(date).toChinese(), ids);
        if (CollectionUtils.isEmpty(attenceConfigs)) return null;

        // 获取用户考勤
        Date startTime = DateUtil.parse(DateUtil.formatDate(date) + " 00:00:00", DatePattern.NORM_DATETIME_PATTERN);
        Date endTime = DateUtil.parse(DateUtil.formatDate(date) + " 23:59:59", DatePattern.NORM_DATETIME_PATTERN);
        AttendanceExample attendanceExample = new AttendanceExample();
        attendanceExample.createCriteria()
                .andUserIdEqualTo(userId).andCreateTimeBetween(startTime, endTime);
        List<Attendance> attendances = attendanceMapper.selectByExample(attendanceExample);
        return convert(attendances, attenceConfigs, date);

    }

    @Override
    @PreAcquireLock(value = ATTENDANCE)
    public String record(RecordPO recordPO, SysUser user) {

        Date date = new Date();

        if (!Objects.equals(RoleEnum.STUDENT.getCode(), user.getRoleId())) {
            throw new GlobalException(ResultEnum.FORBIDDEN.getMessage());
        }

        // 检查并获取配置
        List<AttenceConfigVO> configInfoVOS = checkRecordPO(recordPO.getConfigIds(), date, user);

        // 插入数据库
        configInfoVOS.forEach(infoVO -> {
            Attendance attendance = new Attendance();
            attendance.setClassId(infoVO.getClassId());
            attendance.setCourseId(infoVO.getCourseId());
            attendance.setUserId(user.getUserNum());
            attendance.setCreateTime(date);
            attendanceMapper.insertSelective(attendance);
        });
        return DateUtil.format(date, DatePattern.NORM_TIME_PATTERN);
    }

    private List<AttenceConfigVO> checkRecordPO(List<Integer> configIds, Date date, SysUser user) {

        String week = DateUtil.dayOfWeekEnum(date).toChinese();
        String dateStr = DateUtil.formatDate(date);

        // 获取配置信息
        List<AttenceConfigVO> configInfoVOS = attendanceMapperExt.queryAttenceInfo(configIds);

        // 获取学生班级及课程
        List<ClassCourseVO> classCourseVOS = queryStudentClassCourse(user.getStuorteaId());
        if (CollectionUtils.isEmpty(classCourseVOS)) return null;

        Map<Integer, List<Integer>> map = classCourseVOS.stream()
                .collect(Collectors.groupingBy(
                        ClassCourseVO::getClassId, Collectors.mapping(ClassCourseVO::getCourseId, Collectors.toList())));

        // 用户打卡情况
        List<Attendance> attendances = queryUserAttendances(user.getUserNum(), dateStr);
        Table<Integer, Integer, List<Attendance>> attendanceTable = convertTable(attendances);

        List<AttenceConfigVO> result = Lists.newArrayList();
        boolean clocked = false;
        for (int i = 0; i < configInfoVOS.size(); i++) {
            AttenceConfigVO infoVO = configInfoVOS.get(i);

            // 获取用户班级、课程是否在考勤配置中
            List<Integer> courseIds = map.get(infoVO.getClassId());
            if (CollectionUtils.isEmpty(courseIds) || !courseIds.contains(infoVO.getCourseId())) continue;

            // 开关是否开启
            if (!infoVO.isSwitchOn()) continue;

            // 是否在考勤周期内
            String periodStartTimeStr = DateUtil.formatDate(infoVO.getPeriodStartTime()) + " 00:00:00";
            String periodEndTimeStr = DateUtil.formatDate(infoVO.getPeriodEndTime()) + " 23:59:59";
            if (!isWithin(periodStartTimeStr, periodEndTimeStr, date, DatePattern.NORM_DATETIME_PATTERN)) continue;

            // 星期是否正确
            if (!Objects.equals(infoVO.getWeek(), week)) continue;

            // 是否在时段内
            String startTimeStr = dateStr + " " + infoVO.getStartTime();
            String endTimeStr = dateStr + " " + infoVO.getEndTime();
            if (!isWithin(startTimeStr, endTimeStr, date, DatePattern.NORM_DATETIME_MINUTE_PATTERN)) continue;

            // 用户是否已打卡
            List<Attendance> userAttendances = attendanceTable.get(infoVO.getCourseId(), infoVO.getClassId());
            if (CollectionUtils.isNotEmpty(userAttendances)) {
                List<Attendance> list = userAttendances.stream().filter(attendance ->
                        isWithin(startTimeStr, endTimeStr, attendance.getCreateTime(),
                                DatePattern.NORM_DATETIME_MINUTE_PATTERN)).collect(Collectors.toList());
                if (list.size() > 0) {
                    clocked = true;
                }
                if (Objects.equals(list.size(), userAttendances.size())) continue;
            }
            result.add(infoVO);
        }

        if (clocked && CollectionUtils.isEmpty(result)) {
            throw new GlobalException("您已在指定时间内打卡");
        }
        if (CollectionUtils.isEmpty(result)) {
            throw new GlobalException("未到打卡时间段内");
        }

        return result;

    }

    private List<Attendance> queryUserAttendances(Integer userId, String dateStr) {
        // 获取用户打卡时间
        AttendanceExample example = new AttendanceExample();
        example.createCriteria().andUserIdEqualTo(userId).andCreateTimeBetween(
                DateUtil.parseDateTime(dateStr + " 00:00:00"),
                DateUtil.parseDateTime(dateStr + " 23:59:59"));
        return attendanceMapper.selectByExample(example);
    }

    private Table<Integer, Integer, List<Attendance>> convertTable(List<Attendance> attendances) {
        Table<Integer, Integer, List<Attendance>> attendanceTable = HashBasedTable.create();
        if (CollectionUtils.isEmpty(attendances)) return attendanceTable;

        attendances.forEach(attendance -> {
            List<Attendance> data = attendanceTable.get(attendance.getCourseId(), attendance.getClassId());
            if (CollectionUtils.isEmpty(data)) {
                data = Lists.newArrayList();
            }
            data.add(attendance);
            attendanceTable.put(attendance.getCourseId(), attendance.getClassId(), data);
        });
        return attendanceTable;
    }

    private List<AttenceConfigVO> convert(List<Attendance> attendances,
                                          List<AttenceConfigVO> attenceConfigs, Date time) {

        String date = DateUtil.formatDate(time);

        List<AttenceConfigVO> data = Lists.newArrayList();
        attenceConfigs.forEach(attenceConfig -> {

            List<Attendance> attendanceList = attendances.stream()
                    .filter(attendance -> Objects.equals(attendance.getClassId(), attenceConfig.getClassId())
                            && Objects.equals(attendance.getCourseId(), attenceConfig.getCourseId()))
                    .collect(Collectors.toList());

            if (CollectionUtils.isNotEmpty(attendanceList)) {
                attendanceList.forEach(attendance -> {
                    String startTimeStr = date + " " + attenceConfig.getStartTime();
                    String endTimeStr = date + " " + attenceConfig.getEndTime();
                    attenceConfig.setClocked(
                            isWithin(startTimeStr, endTimeStr, attendance.getCreateTime(),
                                    DatePattern.NORM_DATETIME_MINUTE_PATTERN));
                });
            }

            data.add(attenceConfig);
        });

        return data;

    }

    private boolean isWithin(String startTimeStr, String endTimeStr, Date time, String format) {
        long startTime = DateUtil.parse(startTimeStr, format).getTime();
        long endTime = DateUtil.parse(endTimeStr, format).getTime();
        if (time.getTime() >= startTime && time.getTime() <= endTime) {
            return true;
        }
        return false;
    }

    private List<ClassCourseVO> queryStudentClassCourse(String stuorteaId) {
        return attendanceMapperExt.queryStudentClassCourse(stuorteaId);
    }

}
